package com.bit.cfdi.web.vm;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.zkoss.bind.annotation.AfterCompose;
import org.zkoss.bind.annotation.BindingParam;
import org.zkoss.bind.annotation.ContextParam;
import org.zkoss.bind.annotation.ContextType;
import org.zkoss.bind.annotation.GlobalCommand;
import org.zkoss.bind.annotation.Init;
import org.zkoss.bind.annotation.NotifyChange;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Desktop;
import org.zkoss.zk.ui.Session;
import org.zkoss.zk.ui.select.Selectors;
import org.zkoss.zk.ui.select.annotation.VariableResolver;
import org.zkoss.zk.ui.select.annotation.Wire;
import org.zkoss.zk.ui.select.annotation.WireVariable;
import org.zkoss.zul.Div;
import org.zkoss.zul.Include;
import org.zkoss.zul.Tab;
import org.zkoss.zul.Tabbox;
import org.zkoss.zul.Tabpanel;

import com.bit.cfdi.web.constants.NavInfrastructure;
import com.bit.cfdi.web.domain.ModulosUsuario;
import com.bit.cfdi.web.domain.Negocio;
import com.bit.cfdi.web.domain.Sucursal;
import com.bit.cfdi.web.domain.Usuario;
import com.bit.cfdi.web.services.UsuarioNegocioService;
import com.bit.cfdi.web.session.SessionUtils;

@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver.class)
public class HomeViewModel {
	
	private Logger log = LogManager.getLogger(this.getClass().getName());
	
	//Variables
	private Usuario usuario;
	private Negocio negocio;
	private Sucursal matriz;
	private Sucursal sucursal;
	private String RFC;
	
	@WireVariable
	private SessionUtils sessionUtils;
	@WireVariable
	private UsuarioNegocioService usuarioNegocioService;
	
	//Navigation variables
	//Holder of the modules
	@Wire
	private Tabbox tbxNavegacion;
	//Navigation area
	@Wire
	private Div navigationPanel;
	//Selected module
	private Integer selectedIndex;
	//List that holds current modules available
	private List<String> modules = new ArrayList<String>();
	//User modules
	private List<String> userModules;
	//END Navigation variables
	
	@Init
	public void init(@ContextParam(ContextType.DESKTOP) final Desktop desktop, 
			@ContextParam(ContextType.SESSION) final Session session) {
		
		usuario = (Usuario) sessionUtils.getFromSession(SessionUtils.USUARIO);
		negocio = (Negocio) sessionUtils.getFromSession(SessionUtils.NEGOCIO);
		matriz = getMatrizFromNegocio(negocio);
		sucursal = getSucursalFromNegocio(negocio);
		RFC = getRFCFromNegocio(negocio);
		sessionUtils.addToSession(SessionUtils.MATRIZ, matriz);
		sessionUtils.addToSession(SessionUtils.SUCURSAL, sucursal);
		sessionUtils.addToSession(SessionUtils.RFC, RFC);
	}
	
	@AfterCompose
	public void afterCompose(@ContextParam(ContextType.VIEW) Component view) {
		//Attach .zul components for fulfill navigation depending on permissions
		attachComponents(view);
		
		//just for development purposes
		userModules = getUserModules();
		
		//Map to send variables to all the plugins
		Map<String, Object> variables = getModuleVariables(userModules);
		
		//Add default permissions, the ones that everybody has
		addDefaultModules(userModules);
		//Add module Comprobante only if the user has one of their children
		searchForComprobante(userModules);
		
		//Fill the navigation components
		fillNavigationComponents(userModules, variables);
		
		//Fill current user modules
		fillModulesComponents(userModules, variables);
		
		//Show welcome screen
		changeModule(NavInfrastructure.KEY_BIENVENIDO);
	}
	
	//Getters - Setters
	public Integer getSelectedIndex() {
		return selectedIndex;
	}
	
	public void setSelectedIndex(final Integer selectedIndex) {
		this.selectedIndex = selectedIndex;
	}
	
	//Global Commands
	@GlobalCommand @NotifyChange("selectedIndex")
	public void changeModule(@BindingParam("key") String moduleKey) {
		log.info("Changing to module: "+moduleKey);
		if(moduleKey != null && modules.contains(moduleKey)) {
			selectedIndex = modules.indexOf(moduleKey);
		}
	}
	
	//Private Methods
	private void attachComponents(Component view) {
		Selectors.wireComponents(view, this, false);
	}
	
	private Sucursal getMatrizFromNegocio(final Negocio negocio) {
		for(Sucursal sucursal : negocio.getSucursales()) {
			if(sucursal.getTipo().intValue() == Sucursal.TIPO_MATRIZ) {
				return sucursal;
			}
		}
		return null;
	}
	
	private Sucursal getSucursalFromNegocio(final Negocio negocio) {
		for(Sucursal sucursal : negocio.getSucursales()) {
			if(sucursal.getTipo().intValue() == Sucursal.TIPO_SUCURSAL) {
				return sucursal;
			}
		}
		return null;
	}
	
	private String getRFCFromNegocio(final Negocio negocio) {
		if(negocio.getDatosFiscales() != null) {
			return negocio.getDatosFiscales().getRfc();
		}
		return null;
	}
	
	private List<String> getUserModules() {
		List<String> userModules = new ArrayList<String>();
		Set<ModulosUsuario> modulos = usuarioNegocioService
				.getUsuarioNegocio(usuario, negocio).getAccesos();
		for(ModulosUsuario modulo : modulos) {
			userModules.add(modulo.getNombreModulo());
		}
		return userModules;
	}
	
	private void addDefaultModules(final List<String> userModules) {
		userModules.add(NavInfrastructure.KEY_BIENVENIDO);
		userModules.add(NavInfrastructure.KEY_PERFIL);
		userModules.add(NavInfrastructure.KEY_NEGOCIO);
	}
	
	private Map<String, Object> getModuleVariables(List<String> userModules) {
		Map<String, Object> toReturn = new HashMap<String, Object>();
		toReturn.put("USER_MODULES", userModules);
		return toReturn;
	}
	
	private void searchForComprobante(final List<String> userModules) {
		for(String currentModule : userModules) {
			if(NavInfrastructure.COMPROBANTE_PARTS.contains(currentModule)) {
				userModules.add(NavInfrastructure.KEY_COMPROBANTE);
				break;
			}
		}
	}
	
	private void fillNavigationComponents(final List<String> userModules, 
			final Map<String, Object> args) {
		
		List<Integer> modulesPositions = new ArrayList<Integer>(
				NavInfrastructure.POSITION_KEY_MODULE.keySet());
		Collections.sort(modulesPositions);
		for(Integer currentPosition : modulesPositions) {
			String currentModuleKey = NavInfrastructure.POSITION_KEY_MODULE
					.get(currentPosition);
			if(NavInfrastructure.KEY_MODULE_NAVIGATION_COMPONENT
					.containsKey(currentModuleKey) &&
					userModules.contains(currentModuleKey)) {
				Include navComponent = new Include(
						NavInfrastructure.KEY_MODULE_NAVIGATION_COMPONENT
						.get(currentModuleKey));
				navigationPanel.appendChild(navComponent);
			}
		}
	}

	private void fillModulesComponents(final List<String> userModules, 
			final Map<String, Object> args) {
		
		for(String currentKey : NavInfrastructure.KEY_MODULE_COMPONENT.keySet()) {
			if(userModules.contains(currentKey)){
				Tab tab = new Tab();
				tab.setLabel("");
				tab.setStyle("visibility:hidden");
				tab.setZclass("tabs");
				modules.add(currentKey);
				tbxNavegacion.getTabs().appendChild(tab);
				Tabpanel tabPanel = new Tabpanel();
				tabPanel.setZclass("tabpanel");
				Include tabContent = new Include(
						NavInfrastructure.KEY_MODULE_COMPONENT.get(currentKey));
				tabPanel.appendChild(tabContent);
				tbxNavegacion.getTabpanels().appendChild(tabPanel);
			}
		}
	}
}
